/* machine_hw_i2c.c */

#include "machine_hw_i2c.h"

static const clock_ip_name_t hw_i2c_clock_arr[] = LPI2C_CLOCKS;

STATIC void machine_hw_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind)
{
    machine_hw_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in);
    mp_printf(print, "I2C(%u, freq=%u)", self->i2c_id, self->conf->freq);
}

STATIC void machine_hw_i2c_init(mp_obj_base_t *self_in, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args)
{
    enum
    {
        HW_I2C_INIT_ARG_freq=0,
        HW_I2C_INIT_ARG_timeout
    };
    static const mp_arg_t allowed_args[] =
    {
        [HW_I2C_INIT_ARG_freq]{ MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 10000     } },
        [HW_I2C_INIT_ARG_timeout]{ MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT , {.u_int = 255       } },
    };

    /* 解析参数 */
    mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
    mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

    machine_hw_i2c_obj_t *self = (machine_hw_i2c_obj_t *)self_in;
    if (args[HW_I2C_INIT_ARG_freq].u_int > 400000)
    {
        mp_raise_ValueError(MP_ERROR_TEXT("unavailable param: freq."));
    }
    self->conf->freq = args[HW_I2C_INIT_ARG_freq].u_int;
    self->conf->timeout = args[HW_I2C_INIT_ARG_timeout].u_int;

    /* 配置引脚 */
    PORT_SetPinMux(self->sda_pin_obj->io_port, self->sda_pin_obj->gpio_pin, self->sda_pin_af);
    PORT_SetPinMux(self->scl_pin_obj->io_port, self->scl_pin_obj->gpio_pin, self->scl_pin_af);

    /* 配置i2c时钟源 */
    CLOCK_DisableClock(hw_i2c_clock_arr[self->i2c_id]);
    CLOCK_SetIpSrc(hw_i2c_clock_arr[self->i2c_id], kCLOCK_IpSrcSysPllAsync);
    CLOCK_EnableClock(hw_i2c_clock_arr[self->i2c_id]);

    LPI2C_MasterConf_Type lpi2c_master_conf =
    {
        .BaudrateHz = self->conf->freq, /* 10k. */
        .TxFifoWatermark = 4u,
        .RxFifoWatermark = 0u,
        .EnableInDozenMode = false,
        .EnableInDebugMode = false,
        .EnableIgnoreAck = false,
        .EnableAutoGenSTOP = false,
        .ClockCycleDiv = eLPI2C_ClockCycleDiv_16,
        .BusIdleTimeoutCycles = 0u,
    };
    LPI2C_MasterInit(self->i2c_port, &lpi2c_master_conf, CLOCK_GetIpFreq(hw_i2c_clock_arr[self->i2c_id]));

    //return mp_const_none;
}

STATIC mp_obj_t machine_hw_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args)
{
    mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); /* 1个固定位置参数 */

    const machine_hw_i2c_obj_t *i2c_obj = hw_i2c_find(args[0]);

    if ( (n_args >= 1) || (n_kw >= 0) )
    {
        mp_map_t kw_args;
        mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); /* 将关键字参数从总的参数列表中提取出来，单独封装成kw_args。 */
        machine_hw_i2c_init((mp_obj_base_t *)i2c_obj, n_args - 1, args + 1, &kw_args);
    }

    return (mp_obj_t)i2c_obj;
}

STATIC int machine_hw_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, size_t len, uint8_t *buf, unsigned int flags)
{
    machine_hw_i2c_obj_t *self = (machine_hw_i2c_obj_t *)self_in;

    uint32_t hw_i2c_flags = 0u;
    int ret;

    if (0u == (flags & MP_MACHINE_I2C_FLAG_STOP))
    {
        hw_i2c_flags |= LPI2C_TRANSFER_FLAG_NO_STOP;
    }

    if (flags & MP_MACHINE_I2C_FLAG_READ)
    {
        if (self->conf->timeout)
        {
            ret = LPI2C_MasterReadBlockingTimeout(self->i2c_port, addr, buf, len, hw_i2c_flags, self->conf->timeout);
        }
        else
        {
            ret = LPI2C_MasterReadBlocking(self->i2c_port, addr, buf, len, hw_i2c_flags);
        }
        ret = 0;
    }
    else
    {
        if (self->conf->timeout)
        {
            ret = LPI2C_MasterWriteBlockingTimeout(self->i2c_port, addr, buf, len, hw_i2c_flags, self->conf->timeout);
        }
        else
        {
            ret = LPI2C_MasterWriteBlocking(self->i2c_port, addr, buf, len, hw_i2c_flags);
        }
    }

    return ret;
}

STATIC const mp_machine_i2c_p_t machine_hw_i2c_p =
{
    .transfer = mp_machine_i2c_transfer_adaptor,
    .transfer_single = machine_hw_i2c_transfer_single,
};

const mp_obj_type_t machine_hw_i2c_type =
{
    { &mp_type_type },
    .name = MP_QSTR_I2C,
    .print = machine_hw_i2c_print,
    .make_new = machine_hw_i2c_make_new,
    .protocol = &machine_hw_i2c_p,
    .locals_dict = (mp_obj_dict_t *)&mp_machine_i2c_locals_dict,
};

const machine_hw_i2c_obj_t *hw_i2c_find(mp_obj_t user_obj)
{
    /* 如果传入参数本身就是一个i2c的实例，则直接送出这个i2c。 */
    if ( mp_obj_is_type(user_obj, &machine_hw_i2c_type) )
    {
        return user_obj;
    }

    /* 如果传入参数是一个i2c通道号，则通过索引在i2c清单中找到这个通道，然后送出这个通道。 */
    if ( mp_obj_is_small_int(user_obj) )
    {
        uint8_t i2c_idx = MP_OBJ_SMALL_INT_VALUE(user_obj);
        if ( i2c_idx < machine_hw_i2c_num )
        {
            return machine_hw_i2c_objs[i2c_idx];
        }
    }

    mp_raise_ValueError(MP_ERROR_TEXT("I2C doesn't exist"));
}

/* EOF. */

